ゆるふわシェル問題 その3
全部で8問
そんなに難しくないはず
だいたい1~3コマンドで解ける
使う技術
問題
Q1 中身0byteのファイルaを作成してください
code:a1.sh
touch a
: > a
echo -n "" > a
問題の意図
ダミーファイルが欲しくなるときはそれなりにあるので
空ファイル作る方法はいくつかあるけれど echo > aではだめ
改行文字も含まれてしまう
解説
touchもとはファイルのタイムスタンプを更新するコマンドだけど空ファイルを作るのにも使える
:は何もしないコマンドで、その出力をファイルに書き出している
echo -nは改行なしで出力する
Q2 以下のテキストファイルaの中の文字列をすべて小文字に変換して上書きしてください
code:q2.sh
cat << EOS | tee a
HELLO WORLD
SUSHI
FooBar
EOS
code:a2.sh
mv b a
問題の意図
小文字大文字変換の方法を知る
sedだとめんどくさい
解説
trは処理するデータを標準入力からしか受け付けないので一旦catした結果をパイプで渡す
trには置換のパターンがいくつか用意されているのでそれを使う
この例だと大文字を小文字に変換する
いきなりリダイレクトでファイルaを上書きするとファイルの中身が消える
一旦別ファイルに吐き出してからmvで移動するところまでやって正解
Q3 1 ÷ 3 と -1 ÷ 3 を計算して小数点以下6桁まで出力してください
期待値は以下の通りです
code:q3.expect
0.333333
-0.333333
code:a3.sh
# bc の例
echo 'scale=6; 1/3' | bc | sed -E 's/^(-)?\./\10./'
echo 'scale=6; -1/3' | bc | sed -E 's/^(-)?\./\10./'
# awkの例
awk 'BEGIN{ print 1/3; print -1/3 }'
問題の意図
bashの算術演算式だと小数点以下の計算や出力ができないのでやり方を把握する 解説
bcを使う場合はscaleで小数点以下の桁数を指定する
整数部0が消えるのがトラップ
0を付け足す正規表現を書く
^(-)?\.
^行頭
(-)? -が1つあってもなくても良い
\. .文字 (.は正規表現で使用する特殊な文字なのでエスケープが必要
\10.
\1前述の正規表現でマッチした部分をグルーピングした結果を参照する
0.普通の文字としての0.
awkなら普通にそのまま計算すればよい
Q4 以下の複数のディレクトリ配下でそれぞれmakeしてください
code:a4.sh
mkdir prog_{1,2,3}
echo $'build:\n\techo build 1' > prog_1/Makefile
echo $'build:\n\techo build 2' > prog_2/Makefile
echo $'build:\n\techo build 3' > prog_3/Makefile
find prog_*
code:a4.sh
for d in prog_*; do
(cd $d && make)
done
問題の意図
スクリプトとかで、一旦ディレクトリを移動して戻る処理をどう書くかを考える
こういうふうに書くと失敗する
code:sh
for d in prog_*; do
cd $d
make
done
一旦ディレクトリを移動した先から更に移動しようとするのでダメ
ありがちなミス
解説
サブシェル内部は別のシェルプロセス
別シェルプロセス内でchange directoryしても親プロセスには影響しない
ループ内だけサブシェルにすることで、次のループに影響を残さない
Q5 1コマンドで1 3 5 7 9 ... 20を出力してください
code:a5.sh
awk 'BEGIN{ for(i=0; i<20; i+=2) { print i+1 }; print 20 }'
seq 1 2 20 # NG
echo {1..20..2} 20 # egplさんの解答
問題の意図
seqの使い方 (フェイク)
解説
seqは等差数列を生成する際の交差の値を指定できる
この場合は初項1から20までを交差2で生成する
が、最後の数値20は出力されないのでNG
結局awkで解く
egplさんの解答のほうがスマートで良い
Q6 以下のテキストファイルaの中身を文字化けしないように画面に出力してください
code:q6.sh
echo 'jvWOaQo=' | base64 -d > a
code:a6.sh
# 僕の用意した答え
nkf --guess a
cat a | iconv -f SJIS -t UTF-8
# タイムラインに流れてきた答え
nkf -w a
問題の意図
文字コードを変換する処理
解説
iconv
元はSJISの「寿司」をBase64エンコードしたもの
問題を出題するときにパット見何が書かれてるかわからなくしたくてBase64にした
nkf --guessで文字コードを調べられる
SJISだとわかったのでiconvでSJISをUTF8に変換する
nkf -w
タイムラインに流れてきて知ったけれど-wでも良い
Q7 /etc/passwdの各列の桁数をそろえて人間が読みやすい形で出力してください。不足の列がずれないように配慮してください
code:a7.sh
cat /etc/passwd | sed "s/::/: :/g" | column -s : -t | textimg -s #シェル芸 問題の意図
columnコマンド使うと見やすくなるよ
解説
column -tでいい感じに桁を揃えて出力できる
column -s : で区切り文字を指定できる
ただし、これだけだと/etc/passwdには空の列も存在するので、一部の列がずれる
sedの部分で空白文字を挟むことで解決
Q8 カレントディレクトリの末尾2階層分の文字列をとりだして結合して出力してください
code:q8.sh
echo "例: /work/proj1/app から proj1app という文字列を取り出す"
mkdir -p /work/proj1/app
cd $_
code:a8.sh
pwd | tr / \\n | tail -n 2 | tr -d \\n
問題の意図
この前友人に相談されたことで、それをそのまま問題にしてみた
解き方色々ありそうでどうなるかなぁと
解説
pwdでカレントディレクトリを出力
trでディレクトリの区切り文字を改行文字に置換
改行されたディレクトリ名の末尾2行を取得
改行文字を削除することで結合
以上
余談
と思ったけれどその1もその2もコマンド知ってないと難しそうなのあるな
個人的にはQ7が一番難しいかな、と